﻿namespace Microsoft.Practices.Unity
{
    using System;
    using System.Collections.Generic;
    using System.Web;

    /// <summary>
    /// 
    /// </summary>
    public class UnityPerWebRequestLifetimeModule : IHttpModule, IDisposable
    {
        /// <summary>
        /// 
        /// </summary>
        private static readonly object Key = new object();

        /// <summary>
        /// 
        /// </summary>
        private HttpContext _httpContext;

        /// <summary>
        /// Initializes a new instance of the <see cref="UnityPerWebRequestLifetimeModule"/> class.
        /// </summary>
        /// <param name="httpContext">The HTTP context.</param>
        public UnityPerWebRequestLifetimeModule(HttpContext httpContext)
        {
            _httpContext = httpContext;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="UnityPerWebRequestLifetimeModule"/> class.
        /// </summary>
        public UnityPerWebRequestLifetimeModule()
        {
        }

        /// <summary>
        /// Gets the instances.
        /// </summary>
        internal IDictionary<UnityPerWebRequestLifetimeManager, object> Instances
        {
            get
            {
                _httpContext = (HttpContext.Current != null) ? HttpContext.Current : _httpContext;
                return (_httpContext == null) ? null : GetInstances(_httpContext);
            }
        }

        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application</param>
        public void Init(HttpApplication context)
        {
            context.EndRequest += (sender, e) => RemoveAllInstances();
        }

        /// <summary>
        /// Gets the instances.
        /// </summary>
        /// <param name="httpContext">The HTTP context.</param>
        /// <returns></returns>
        internal static IDictionary<UnityPerWebRequestLifetimeManager, object> GetInstances(HttpContext httpContext)
        {
            IDictionary<UnityPerWebRequestLifetimeManager, object> instances;

            if (httpContext.Items.Contains(Key))
            {
                instances = (IDictionary<UnityPerWebRequestLifetimeManager, object>)httpContext.Items[Key];
            }
            else
            {
                lock (httpContext.Items)
                {
                    if (httpContext.Items.Contains(Key))
                    {
                        instances = (IDictionary<UnityPerWebRequestLifetimeManager, object>)httpContext.Items[Key];
                    }
                    else
                    {
                        instances = new Dictionary<UnityPerWebRequestLifetimeManager, object>();
                        httpContext.Items.Add(Key, instances);
                    }
                }
            }

            return instances;
        }

        /// <summary>
        /// Removes all instances.
        /// </summary>
        internal void RemoveAllInstances()
        {
            IDictionary<UnityPerWebRequestLifetimeManager, object> instances = Instances;

            if (!instances.IsNullOrEmpty())
            {
                foreach (KeyValuePair<UnityPerWebRequestLifetimeManager, object> entry in instances)
                {
                    IDisposable disposable = entry.Value as IDisposable;

                    if (disposable != null)
                    {
                        disposable.Dispose();
                    }
                }

                instances.Clear();
            }
        }

        #region IDisposable Members

        /// <summary>
        /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
        /// </summary>
        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}